package orchestrationstack

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	yamlConvert "github.com/ghodss/yaml"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zhs "github.com/go-playground/validator/v10/translations/zh"
	"github.com/gophercloud/gophercloud"
	volumes3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources"
	"github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks"
	"github.com/sirupsen/logrus"
	"openstack_api/client"
	myCommon "openstack_api/common"
	"openstack_api/exceptions"
	"openstack_api/object"
	"strconv"
	"strings"
	"time"
)

type OrchestrationStack struct {
	ID              string                     `json:"id" gorm:"primaryKey"`
	Name            string                     `json:"name" gorm:"name" binding:"required"`
	DefaultNetwork  string                     `json:"default_network,omitempty" gorm:"default_network" binding:"omitempty"`
	Networks        []NetworkParam             `json:"networks" gorm:"networks" binding:"omitempty"`
	DisableRollback *bool                      `json:"disable_rollback" gorm:"disable_rollback" binding:"omitempty"`
	Servers         []ServerParam              `json:"servers" gorm:"servers" binding:"required"`
	Containers      []ContainerParam           `json:"containers" gorm:"containers" binding:"omitempty"`
	Routers         []RouterParam              `json:"routers" gorm:"routers" binding:"omitempty"`
	CallBackUrl     string                     `json:"callback_url" gorm:"callback_url" binding:"omitempty,url"`
	Template        object.Template            `json:"-" gorm:"-"`
	Client          *gophercloud.ServiceClient `json:"-"`
}

//Flavor          string                     `json:"flavor" gorm:"flavor" binding:"omitempty"`
//Flavors         []FlavorParam              `json:"flavors" gorm:"flavors"`

type FlavorParam struct {
	Name     string `json:"name"`
	Id       string `json:"id"`
	Vcpus    string `json:"vcpus"`
	Ram      string `json:"mem"`
	IsPublic *bool  `json:"isPublic"`
	Disk     string `json:"disk"`
}

type ServerParam struct {
	//Flavor    string               `json:"flavor"`
	Volumes   []ServerVolumeParam  `json:"volumes" validate:"required"`
	Networks  []ServerNetworkParam `json:"networks" validate:"required"`
	Name      string               `json:"name" validate:"required"`
	KeyName   string               `json:"key_name,omitempty"`
	AdminPass string               `json:"admin_pass,omitempty"`
	Userdata  string               `json:"userdata,omitempty"`
	Host      string               `json:"host,omitempty"`
	Image     string               `json:"image,omitempty"`
	Cpu       int                  `json:"cpu" validate:"required"`
	Memory    int                  `json:"memory" validate:"required"`
}

type ServerNetworkParam struct {
	Network           string `json:"network"`
	FixedIP           string `json:"fixed_ip,omitempty"`
	FloatingIPNetwork string `json:"floating_ip_network,omitempty"`
	FloatingIPAddress string `json:"floating_ip_address,omitempty"`
	//FloatingIP        bool   `json:"floating_ip,omitempty"`
}

type ServerVolumeParam struct {
	Image               string `json:"image,omitempty"`
	Blank               string `json:"blank,omitempty"`
	Volume              string `json:"volume,omitempty"`
	BFV                 string `json:"boot_from_volume,omitempty"`
	VolumeSize          int    `json:"volume_size"`
	BootIndex           int    `json:"boot_index"`
	DeleteOnTermination string `json:"delete_on_termination,omitempty"`
}

type NetworkParam struct {
	Name           string              `json:"name"`
	Cidr           string              `json:"cidr"`
	GatewayIP      string              `json:"gateway_ip,omitempty"`
	DnsNameservers []string            `json:"dns_nameservers,omitempty"`
	NetworkGateway []NetworkGateway    `json:"network_gateway,omitempty"`
	EnableDhcp     string              `json:"enable_dhcp"`
	HostRoutes     []subnets.HostRoute `json:"host_routes,omitempty"`
	Router         *RouterParam        `json:"router,omitempty"`
	QosPolicy      string              `json:"qos_policy,omitempty"`
	//Subnet          []Subnet `json:"-"`
}

type RouterParam struct {
	Name            string          `json:"name,omitempty"`
	EnableSnat      string          `json:"enable_snat,omitempty"`
	ExternalNetwork string          `json:"external_network,omitempty"`
	ExtraRoute      []routers.Route `json:"extra_route,omitempty"`
}

type NetworkGateway struct {
	External                bool   `json:"external"`
	ProviderNetworkType     string `json:"provider_network_type"`
	ProviderPhysicalNetwork string `json:"provider_physical_network"`
}

type stackRespnse struct {
	Code        int              `json:"code"`
	Title       string           `json:"title"`
	Explanation string           `json:"explanation"`
	Error       stackResponseErr `json:"error"`
}

type stackResponseErr struct {
	Type      string `json:"type"`
	Traceback string `json:"traceback"`
	Message   string `json:"message"`
}

type ContainerParam struct {
	CPU           int                  `json:"cpu"`
	Memory        int                  `json:"memory"`
	Image         string               `json:"image"`
	Hints         string               `json:"hints,omitempty"`
	Environment   map[string]string    `json:"environment,omitempty"`
	RestartPolicy string               `json:"restart_policy,omitempty"`
	Networks      []ServerNetworkParam `json:"networks"`
	Name          string               `json:"name"`
}

//type resourcesResponse struct {
//}

//type Subnet struct {
//}
//
//type Port struct {
//}
//
//type NetworkResources struct {
//}

type StackResourceType string

var (
	CreateInProgress = "CREATE_IN_PROGRESS"

	CreateComplete = "CREATE_COMPLETE"

	UpdateComplete = "UPDATE_COMPLETE"
)

var (
	//validate = validator.New()          // 实例化验证器
	chinese  = zh.New()                 // 获取中文翻译器
	uni      = ut.New(chinese, chinese) // 设置成中文翻译器
	trans, _ = uni.GetTranslator("zh")  // 获取翻译字典
)

//
//var (
//	default_network = "private"
//)

func (s OrchestrationStack) getClient() (serviceClient *gophercloud.ServiceClient) {

	serviceClient = client.GetOrchestrationClient()
	return

}

// 查找stack
func (s OrchestrationStack) findStack(stackName string) (*stacks.RetrievedStack, error) {
	orcheClient := s.getClient()
	extract, err := stacks.Find(orcheClient, stackName).Extract()
	return extract, err
}

// 组合错误
func (s OrchestrationStack) errRet(c *gin.Context, code int, message interface{}) {
	c.JSON(code, gin.H{
		"code":    code,
		"message": message,
	})
}

// 列出所有或查找单个资源
func (s OrchestrationStack) listStackResources(stackName string, stackID string) (allResources []stackresources.Resource, err error) {
	orchclient := s.getClient()
	resourcePages, err := stackresources.List(orchclient, stackName, stackID, stackresources.ListOpts{}).AllPages()
	if err != nil {
		return nil, err
	}
	resources, err := stackresources.ExtractResources(resourcePages)
	if err != nil {
		return nil, err
	}
	for _, resource := range resources {
		// Get information of a resource in stack
		res := stackresources.Get(orchclient, stackName, stackID, resource.Name)
		if res.Err != nil {
			return nil, res.Err
		}
		resD, err := res.Extract()
		if err != nil {
			return nil, err
		}
		//fmt.Println("Resource Name: ", resD.Name, ", Physical ID: ", resD.PhysicalID, ", Status: ", resD.Status)
		allResources = append(allResources, *resD)
	}
	return
}

// 获取单个资源
func (s OrchestrationStack) getStackResource(stackName string, stackID string, resourceName string) (Resource *stackresources.Resource, err error) {
	orchClient := s.getClient()

	Resource, err = stackresources.Get(orchClient, stackName, stackID, resourceName).Extract()
	if err != nil {
		return nil, err
	}

	return
}

// ListResources GetStack 获取stack_resources
func (s OrchestrationStack) ListResources(c *gin.Context) {
	//uq := c.Request.URL.Query()

	stackName := c.Param("name")
	resourceName := c.Param("resource")
	if stackName != "" {
		stack, err := s.findStack(stackName)
		if err != nil {
			s.errRet(c, 404, fmt.Sprintf("无法找到名为'%s'的栈", stackName))
			return
		}
		if resourceName != "" {
			resource, err := s.getStackResource(stack.Name, stack.ID, resourceName)
			if err != nil {
				s.errRet(c, 500, err.Error())
				return
			}
			c.JSON(200, resource)
			return
		}
		//stacks.ListOpts{}
		allResources, err := s.listStackResources(stack.Name, stack.ID)
		if err != nil {
			s.errRet(c, 500, err)
			return
		}

		c.JSON(200, allResources)
	}

}

func (s OrchestrationStack) CreateCallback(callBackUrl string, stack *stacks.RetrievedStack, expectStatus string) {
	times := 0
	logrus.Info("Stack callback start: stack name=", stack.Name)
	for {
		time.Sleep(time.Second)
		times += 1
		stack, _ := s.findStack(stack.Name)
		if stack == nil {
			return
		}
		logrus.Info("Stack callback start: stack name=", stack.Name, ", current status=", stack.Status)
		if stack.Status != expectStatus {
			if strings.Contains(stack.Status, "PROGRESS") {
				s.callback(callBackUrl, stack)
				continue
			}

			if (times >= 1800) || strings.Contains(stack.Status, "ERROR") || strings.Contains(stack.Status, "FAILED") {
				s.callback(callBackUrl, stack)
				break
			}
		} else {
			break
		}
	}
	s.callback(callBackUrl, stack)

}

func (s OrchestrationStack) callback(callBackUrl string, stack *stacks.RetrievedStack) {
	logrus.Debug("Stack callback start: stack name=", stack.Name, ", stack create complete, current status=", stack.Status, ", ready to callback")
	da := s.createCallbackData(stack)
	logrus.Info("Stack callback start: stack name=", stack.Name, ", callback data=", da)
	data, _ := json.Marshal(da)
	logrus.Debug("Stack callback start: callback_data=", string(data))
	body, statuscode, header, e := myCommon.HttpPostJson(data, callBackUrl)
	if statuscode != 200 {
		logrus.Error("Stack callback error: stack name=", stack.Name, ",status_code=", statuscode, ", response=", string(body), ",header=", header, ", error=", e)
	}
	//logrus.Info("Stack callback error: stack name=", stack.Name, ",status_code=", statuscode, ", response=", body, ",header=", header, ", error=", e)
}

func (s OrchestrationStack) GetCallbackData(c *gin.Context) {
	stackName := c.Param("name")
	if stackName != "" {
		stack, err := s.findStack(stackName)
		if err != nil {
			s.errRet(c, 404, fmt.Sprintf("无法找到名为'%s'的栈", stackName))
			return
		}
		callback := s.createCallbackData(stack)
		c.JSON(200, callback)
	}

}

func (s OrchestrationStack) createCallbackData(stack *stacks.RetrievedStack) (ret map[string]interface{}) {
	resources, _ := s.listStackResources(stack.Name, stack.ID)
	ret = make(map[string]interface{})
	var servers []map[string]interface{}
	var networks []map[string]interface{}
	var ports []map[string]interface{}
	var routes []map[string]interface{}
	var routerInterfaces []map[string]interface{}
	var subs []map[string]interface{}

	for _, resource := range resources {
		r := resource.Attributes
		r["status"] = resource.Status
		r["status_reason"] = resource.StatusReason
		if resource.Type == "OS::Nova::Server" {
			r["block_device_mapping_v2"] = s.getServerBdms(r["id"])
			r["OS-EXT-SRV-ATTR:user_data"] = ""
			servers = append(servers, r)
		}
		if resource.Type == "OS::Neutron::Net" {
			networks = append(networks, r)
		}
		if resource.Type == "OS::Neutron::Subnet" {
			subs = append(subs, r)
		}
		if resource.Type == "OS::Neutron::Port" {
			ports = append(ports, r)
		}
		if resource.Type == "OS::Neutron::Router" {
			routes = append(routes, r)
		}
		if resource.Type == "OS::Neutron::RouterInterface" {
			routerInterfaces = append(routerInterfaces, r)
		}
	}

	ret["id"] = stack.ID
	ret["networks"] = networks
	ret["subnets"] = subs
	ret["servers"] = servers
	ret["ports"] = ports
	ret["routers"] = routes
	ret["router_interfaces"] = routerInterfaces
	ret["stack_name"] = stack.Name
	ret["status"] = stack.Status
	ret["status_reason"] = stack.StatusReason
	return
}

//func (s OrchestrationStack) reformCallbackNetworks(networks []map[string]interface{}, subnets []map[string]interface{}) {
//
//}

//GetStack 获取stack
func (s OrchestrationStack) GetStack(c *gin.Context) {
	//uq := c.Request.URL.Query()
	stackName := c.Param("name")
	if stackName != "" {
		stack, err := s.findStack(stackName)
		if err != nil {
			s.errRet(c, 404, fmt.Sprintf("无法找到名为'%s'的栈", stackName))
			return
		} else {
			c.JSON(200, stack)
			return
		}
	}
}

func (s OrchestrationStack) DeleteStack(c *gin.Context) {
	//uq := c.Request.URL.Query()
	stackName := c.Param("name")
	if stackName != "" {
		stack, err := s.findStack(stackName)
		if err != nil {
			s.errRet(c, 404, fmt.Sprintf("无法找到名为'%s'的栈", stackName))
			return
		} else {
			_, err := s.deleteStack(stack.Name, stack.ID)
			if err != nil {
				s.errRet(c, 400, err.Error())
				return
			}
			c.JSON(202, stack)
		}

	}
}

func (s OrchestrationStack) UpdateStack(c *gin.Context) {
	//uq := c.Request.URL.Query()
	logrus.Info("Update stack: get started")
	stackName := c.Param("name")
	if stackName != "" {
		stack, err := s.findStack(stackName)
		if err != nil {
			logrus.Error("Update stack: can't find stack '%s'", stackName)
			s.errRet(c, 404, fmt.Sprintf("Update stack: can't find stack '%s'", stackName))
			return
		} else {
			_, ok := binding.Validator.Engine().(*validator.Validate)
			if ok {
				//err := validate.RegisterValidation("servers", checkServers)
				if err != nil {
					logrus.Error("Update stack: stack '%s', params validate error, '%s'", stackName, err.Error())
					s.errRet(c, 500, err.Error())
					return
				}
				err = c.ShouldBindJSON(&s)
				//err := validate(&s)
				if err != nil {
					logrus.Error("Update stack: stack '%s', params validate error, '%s'", stackName, err.Error())
					s.errRet(c, 500, err.Error())
					return
				}

				jsonTemplate, err := s.getJsonTemplate()
				//fmt.Println(jsonTemplate)
				if err != nil {
					logrus.Error("Update stack: stack '%s', get json template error, '%s'", stackName, err.Error())
					s.errRet(c, 500, err.Error())
					return
				}
				yamlTemplate, err := yamlConvert.JSONToYAML(jsonTemplate)
				if err != nil {
					logrus.Error("Update stack: stack '%s', convert json template to yaml template error, '%s'", stackName, err.Error())
					s.errRet(c, 500, err.Error())
					return
				}
				updateOpts := stacks.UpdateOptsBuilder(stacks.UpdateOpts{
					TemplateOpts: &stacks.Template{TE: stacks.TE{
						Bin: yamlTemplate,
					}},
				})
				logrus.Error("Update stack: stack '%s',  ready to update stack, template \n '%s'", stackName, string(yamlTemplate))
				res := s.updateStack(stack.Name, stack.ID, updateOpts)
				if res.Err != nil {
					logrus.Error("Update stack: stack '%s', template update error, '%s'", stackName, err.Error())
					s.errRet(c, 400, err.Error())
					return
				}
				c.JSON(202, nil)
			}
			go s.CreateCallback(s.CallBackUrl, stack, CreateComplete)
		}

	} else {
		logrus.Error("Update stack: stack name can't be ''")
		s.errRet(c, 400, "Update stack: stack name can't be ''")
	}

}

func (s OrchestrationStack) updateStack(stackName string, stackID string, opts stacks.UpdateOptsBuilder) (res stacks.UpdateResult) {
	orchClient := s.getClient()
	res = stacks.Update(orchClient, stackName, stackID, opts)
	return res
}

func (s OrchestrationStack) deleteStack(stackName string, stackID string) (isDel bool, err error) {
	orchClient := s.getClient()
	result := stacks.Delete(orchClient, stackName, stackID)
	if result.Err != nil {
		return false, result.Err
	}
	return true, nil
}

// CreateStack 创建stack
func (s OrchestrationStack) CreateStack(c *gin.Context) {
	logrus.Info("Create stack: get started")

	//fmt.Println(data)
	orchClient := s.getClient()
	validate, _ := binding.Validator.Engine().(*validator.Validate)
	_ = zhs.RegisterDefaultTranslations(validate, trans)

	err := c.ShouldBindBodyWith(&s, binding.JSON)
	logrus.Info(fmt.Sprintf("header=%s, body=%s, method=%s", c.Request.Header, s, c.Request.Method))
	if err == nil {
		err = s.validateAll()
	}
	if err != nil {
		logrus.Error("Create stack: validate params error, ", err.Error())
		//transletError := exceptions.Translate(validate, err)
		s.errRet(c, 500, err.Error())
		return
	}
	//fmt.Println(reflect.TypeOf(s.Servers))

	jsonTemplate, err := s.getJsonTemplate()
	if err != nil {
		logrus.Error("Create stack: get json template error, ", err.Error())
		s.errRet(c, 500, err.Error())
		return
	}
	//fmt.Println(string(jsonTemplate))

	yamlTemplate, err := yamlConvert.JSONToYAML(jsonTemplate)
	if err != nil {
		logrus.Error("Create stack: convert json template to yaml temaplte error, ", err.Error(), s.Name)
		s.errRet(c, 500, err.Error())
		return
	}
	logrus.Info("Create stack: convert template succeed\n", string(jsonTemplate))

	//fmt.Println(string(yamlTemplate))

	//创建stack
	createOpts := stacks.CreateOptsBuilder(stacks.CreateOpts{
		Name:            s.Name,
		DisableRollback: s.DisableRollback,
		TemplateOpts: &stacks.Template{TE: stacks.TE{
			Bin: yamlTemplate,
		}},
	})
	logrus.Info("Create stack: create opts pass , ready to create, ", s.Name)

	//ret, err := s.findStack(s.Name)
	//if ret != nil {
	//	s.deleteStack(ret.Name, ret.ID)
	//	s.waitStackDelete(ret.Name)
	//}

	result := stacks.Create(orchClient, createOpts)
	createStack, err := result.Extract()
	if err != nil {
		logrus.Error("Create stack: failed to create stack, ", err.Error(), s.Name)
		var errorDetail exceptions.StackErrorDetails
		extractErr, code := exceptions.StackExtractErrorInto(err, &errorDetail)
		if extractErr != nil {
			s.errRet(c, code, extractErr)
			return
		}

		s.errRet(c, errorDetail.Code, errorDetail.Error.Message)
		return
	}
	stack, _ := s.findStack(s.Name)
	logrus.Info("Create stack: succeed to create stack, ready to wait callback, ", s.Name)
	go s.CreateCallback(s.CallBackUrl, stack, CreateComplete)
	c.JSON(200, createStack)

}

func (s OrchestrationStack) waitStackDelete(name string) {
	for {
		time.Sleep(time.Second * 2)
		ret, _ := s.findStack(name)
		if ret == nil {
			break
		}
	}
}

//错误的用法
func (s OrchestrationStack) reformStackError(splitError []string) (code int, message string) {
	//error := stackError.Error()
	errJson := splitError[len(splitError)-1]
	errorMap := stackRespnse{}
	errQ := json.Unmarshal([]byte(errJson), &errorMap)
	if errQ != nil {
		return 500, errQ.Error()
	}
	return errorMap.Code, errorMap.Error.Message
}

// 获取stack的template模板资源，将map转换为json后返回
func (s OrchestrationStack) getJsonTemplate() ([]byte, error) {
	Resources := map[string]object.TemplateResource{}
	newRouters := make(map[string]string)
	newNetworks := make(map[string]string)

	for _, router := range s.Routers {
		routerResource := s.getRouter(router)
		for key, value := range routerResource {
			Resources[key] = value
		}
		newRouters[router.Name] = "1"
	}

	for _, network := range s.Networks {
		//fmt.Println(network)
		networkResource := s.getNetwork(network, newRouters)
		for key, value := range networkResource {
			Resources[key] = value
		}
		newNetworks[network.Name] = "1"
	}

	for _, server := range s.Servers {
		//fmt.Println(server)
		serverResource := s.getServerResource(server, newNetworks)
		for key, value := range serverResource {
			Resources[key] = value
		}
	}

	for _, container := range s.Containers {
		//fmt.Println(container)
		containerResource := s.getContainerResource(container, newNetworks)
		for key, value := range containerResource {
			Resources[key] = value
		}
	}

	s.Template.Resources = Resources
	s.Template.HeatTemplateVersion = "2021-04-16"

	//先转换为json，然后转换为yaml
	jsonTemplate, err := json.Marshal(s.Template)
	if err != nil {
		return []byte(nil), err
	}
	//fmt.Println(string(jsonTemplate))

	return jsonTemplate, nil

}

//根据参数获取要创建的路由资源
func (s OrchestrationStack) getRouter(router RouterParam) (resources map[string]object.TemplateResource) {
	routerName := router.Name
	resources = make(map[string]object.TemplateResource)
	resources[routerName] = object.TemplateResource{
		Type: "OS::Neutron::Router",
		Properties: object.Router{
			Name: routerName,
			ExternalGatewayInfo: object.ExternalGatewayInfo{
				EnableSNAT: router.EnableSnat,
				Network:    router.ExternalNetwork,
			},
		},
	}
	return
}

//func (s OrchestrationStack)GetPort(port PortParam)()

// 根据参数获取要创建网络资源
func (s OrchestrationStack) getNetwork(network NetworkParam, newRouters map[string]string) (resources map[string]object.TemplateResource) {
	resources = make(map[string]object.TemplateResource)
	networkName := network.Name
	//namePrefix := fmt.Sprintf("%s-", s.Name)
	resources[networkName] = object.TemplateResource{
		Type: "OS::Neutron::Net",
		Properties: object.Network{
			Name:      networkName,
			QosPolicy: network.QosPolicy,
		},
	}

	subnetName := fmt.Sprintf("%s-subnet", networkName)
	if network.GatewayIP == "" {
		network.GatewayIP = myCommon.GetGatewayFromCIDR(network.Cidr)
	}

	resources[subnetName] = object.TemplateResource{
		Type: "OS::Neutron::Subnet",
		Properties: object.Subnet{
			Name:       subnetName,
			EnableDhcp: network.EnableDhcp,
			GatewayIP:  network.GatewayIP,
			HostRoutes: network.HostRoutes,
			Network: object.GetResource{
				GetResource: networkName,
			},
			DnsNameservers: network.DnsNameservers,
			Cidr:           network.Cidr,
		},
	}

	if network.Router != nil {
		_, ok := newRouters[network.Router.Name]
		routerName := ""
		if !ok {
			routerName = fmt.Sprintf("%s-router", networkName)
			resources[routerName] = object.TemplateResource{
				Type: "OS::Neutron::Router",
				Properties: object.Router{
					Name: routerName,
					ExternalGatewayInfo: object.ExternalGatewayInfo{
						EnableSNAT: network.Router.EnableSnat,
						Network:    network.Router.ExternalNetwork,
					},
				},
			}
		} else {
			routerName = network.Router.Name
		}

		// subnet gateway port name
		subnetGPName := fmt.Sprintf("%s-subnet-gateway-port", networkName)
		var fixedips []object.FixedIPOpts
		fixedips = append(fixedips, object.FixedIPOpts{
			IPAddress: network.GatewayIP,
			Subnet: &object.GetResource{
				GetResource: subnetName,
			},
		})
		resources[subnetGPName] = object.TemplateResource{
			Type: "OS::Neutron::Port",

			Properties: object.Port{
				Name: subnetGPName,
				//Network: &object.GetResource{
				//	GetResource: networkName,
				//},
				Network:  map[string]string{"get_resource": networkName},
				FixedIPs: fixedips,
			},
		}

		routerGatewayName := fmt.Sprintf("%s-router-gateway", networkName)
		resources[routerGatewayName] = object.TemplateResource{
			Type: "OS::Neutron::RouterInterface",
			Properties: object.RouterInterface{
				Port: &object.GetResource{
					GetResource: subnetGPName,
				},
				Router: &object.GetResource{
					GetResource: routerName,
				},
			},
		}
	}

	return
}

// 根据参数获取创建虚拟机资源
func (s OrchestrationStack) getServerResource(server ServerParam, newNetworks map[string]string) (resources map[string]object.TemplateResource) {
	logrus.Info("Get server: user_data=", server.Userdata)
	serverNetworks, networkResource := s.getServerNetworkResources(server, newNetworks)
	resources = make(map[string]object.TemplateResource)

	serverBLKs, volumeResources := s.getServerBLKs(server)
	metadata := s.getServerMetadata(server)

	// 根据CPU、MEMORY配置创建新的配置
	newFlavors := make(map[string]object.TemplateResource)
	flavorName := s.getFlavorByServer(server, newFlavors)
	//namePrefix := fmt.Sprintf("%s-", s.Name)
	var userDataString string

	if server.Userdata != "" {
		userDataDecode, err := base64.StdEncoding.DecodeString(server.Userdata)
		//logrus.Info("Get server: user_data err=", err, ",decode=", userDataDecode)
		if err == nil {
			//logrus.Info("Get server: user_data err=", err, ",decode=", userDataDecode)
			userDataString = string(userDataDecode)
			//logrus.Info("Get server: user_data=", userDataString)
		}
	}
	logrus.Info("Get server: user_data=", userDataString)
	resource := object.TemplateResource{
		Properties: object.Server{
			KeyName: server.KeyName,
			//Metadata: &s.getServerMetadata(server),
			Flavor:               map[string]string{"get_resource": flavorName},
			Networks:             &serverNetworks,
			UserData:             userDataString,
			BlockDeviceMappingV2: &serverBLKs,
			Metadata:             &metadata,
			Host:                 server.Host,
			Image:                server.Image,
			Name:                 server.Name,
		},
		Type: "OS::Nova::Server",
	}
	resources[server.Name] = resource

	for key, value := range volumeResources {
		resources[key] = value
	}

	for key, value := range networkResource {
		resources[key] = value
	}
	for key, value := range newFlavors {
		resources[key] = value
	}
	return
}

// 根据参数获取创建容器资源
func (s OrchestrationStack) getContainerResource(container ContainerParam, newNetworks map[string]string) (resources map[string]object.TemplateResource) {
	containerNetworks, networkResource := s.getContainerNetworkResources(container, newNetworks)
	resources = make(map[string]object.TemplateResource)
	resource := object.TemplateResource{
		Properties: object.Container{
			Name:          container.Name,
			Cpu:           container.CPU,
			Hints:         container.Hints,
			Image:         container.Image,
			ImageDriver:   "glance",
			Interactive:   "true",
			Memory:        container.Memory,
			Networks:      &containerNetworks,
			RestartPolicy: "always",
			Tty:           "true",
		},
		Type: "OS::Zun::Container",
	}
	resources[container.Name] = resource
	for key, value := range networkResource {
		resources[key] = value
	}
	return
}

// 根据参数获取虚拟机的磁盘映射
func (s OrchestrationStack) getServerBLKs(server ServerParam) (blks []object.BlockDeviceMappingV2, resources map[string]object.TemplateResource) {
	resources = make(map[string]object.TemplateResource)
	for index, volume := range server.Volumes {
		if volume.Volume != "" {
			if volume.BFV != "true" {
				volumeName := fmt.Sprintf("%s-volume-m%d", server.Name, index)
				s.getCloneVolume(volumeName, volume, resources)
				blks = append(blks, object.BlockDeviceMappingV2{
					VolumeID:            object.GetResource{GetResource: volumeName},
					VolumeSize:          volume.VolumeSize,
					DeleteOnTermination: volume.DeleteOnTermination,
				})
			} else {
				blks = append(blks, object.BlockDeviceMappingV2{
					VolumeID:            volume.Volume,
					VolumeSize:          volume.VolumeSize,
					DeleteOnTermination: "false",
				})
			}
		} else {
			blks = append(blks, object.BlockDeviceMappingV2{
				Image:               volume.Image,
				Blank:               volume.Blank,
				BootIndex:           volume.BootIndex,
				VolumeSize:          volume.VolumeSize,
				DeleteOnTermination: volume.DeleteOnTermination,
			})
		}

	}
	return
}

// 如果是从云盘复制启动，则需要先获取一个云盘参数
func (s OrchestrationStack) getCloneVolume(volumeName string, volume ServerVolumeParam, resources map[string]object.TemplateResource) {
	resources[volumeName] = object.TemplateResource{
		Properties: volumes3.CreateOpts{
			Name:        volumeName,
			Size:        volume.VolumeSize,
			SourceVolID: volume.Volume,
		},
		Type: "OS::Cinder::Volume",
	}
	return
}

// 根据参数获取虚拟机的metadata文件
func (s OrchestrationStack) getServerMetadata(server ServerParam) (metadata object.ServerMetadata) {
	if server.AdminPass == "" {
		server.AdminPass = "P@ssword"
	}
	metadata.AdminPass = server.AdminPass
	return
}

// 根据虚拟机的CPU、MEMORY创建新的CPU
func (s OrchestrationStack) getFlavorByServer(server ServerParam, resources map[string]object.TemplateResource) (flavorName string) {
	stackName := s.Name
	cpu := server.Cpu
	memory := server.Memory
	flavorName = fmt.Sprintf("%s-c%d-m%d", stackName, cpu, memory)

	resource := object.TemplateResource{
		Properties: object.Flavor{
			Name:     flavorName,
			FlavorID: flavorName,
			Vcpus:    strconv.Itoa(cpu),
			Ram:      strconv.Itoa(memory),
			Disk:     "20",
		},
		Type: "OS::Nova::Flavor",
	}

	resources[flavorName] = resource
	return
}

// 根据参数获取虚拟机的网络映射
func (s OrchestrationStack) getServerNetworkResources(server ServerParam, newNetworks map[string]string) (serverNetworks []object.ServerNetwork, resources map[string]object.TemplateResource) {
	resources = make(map[string]object.TemplateResource)
	for index, net1 := range server.Networks {
		var Port object.Port

		var fixedIP []object.FixedIPOpts
		if net1.FixedIP != "" {
			fixedIP = append(fixedIP, object.FixedIPOpts{
				IPAddress: net1.FixedIP,
			})
		}
		Port.FixedIPs = fixedIP

		//获取网络是不是新建的，如果在新建网络列表中，则get_resource并赋值给network，如果不是，则是原有网络，则直接将名称赋值给networkID
		_, ok := newNetworks[net1.Network]
		if !ok {
			Port = object.Port{
				NetworkID: net1.Network,
			}

		} else {
			Port = object.Port{
				//Network: &object.GetResource{GetResource: net1.Network},
				Network: map[string]string{"get_resource": net1.Network},
			}
		}
		//namePrefix := fmt.Sprintf("%s-", s.Name)
		portName := fmt.Sprintf("%s-port%d", server.Name, index)
		Port.Name = portName
		resources[portName] = object.TemplateResource{
			Type:       "OS::Neutron::Port",
			Properties: Port,
		}

		if net1.FloatingIPNetwork != "" || net1.FloatingIPAddress != "" {
			FIP := object.FloatingIP{
				PortID: object.GetResource{
					GetResource: portName,
				},
				FloatingNetworkID: net1.FloatingIPNetwork,
				FloatingIPAddress: net1.FloatingIPAddress,
			}
			floatingIPName := fmt.Sprintf("%s_floating_ip%d", server.Name, index)
			resources[floatingIPName] = object.TemplateResource{
				Type:       "OS::Neutron::FloatingIP",
				Properties: FIP,
			}
		}
		serverNetworks = append(serverNetworks, object.ServerNetwork{Port: object.GetResource{GetResource: portName}})
	}
	return
	//object.ServerNetworkParam{NetworkParam: server.Networks}
}

// 根据参数获取容器的网络映射,这个目前看来与虚拟机的源码是相同的。
func (s OrchestrationStack) getContainerNetworkResources(container ContainerParam, newNetworks map[string]string) (serverNetworks []object.ServerNetwork, resources map[string]object.TemplateResource) {
	resources = make(map[string]object.TemplateResource)
	for index, net1 := range container.Networks {
		var Port object.Port

		var fixedIP []object.FixedIPOpts
		if net1.FixedIP != "" {
			fixedIP = append(fixedIP, object.FixedIPOpts{
				IPAddress: net1.FixedIP,
			})
		}
		Port.FixedIPs = fixedIP

		//获取网络是不是新建的，如果在新建网络列表中，则get_resource并赋值给network，如果不是，则是原有网络，则直接将名称赋值给networkID
		_, ok := newNetworks[net1.Network]
		if !ok {
			Port = object.Port{
				Network: net1.Network,
			}

		} else {
			Port = object.Port{
				Network: map[string]string{"get_resource": net1.Network},
			}
		}
		//namePrefix := fmt.Sprintf("%s-", s.Name)
		portName := fmt.Sprintf("%s-port%d", container.Name, index)
		Port.Name = portName
		resources[portName] = object.TemplateResource{
			Type:       "OS::Neutron::Port",
			Properties: Port,
		}

		if net1.FloatingIPNetwork != "" {
			FIP := object.FloatingIP{
				PortID: object.GetResource{
					GetResource: portName,
				},
				FloatingNetworkID: net1.FloatingIPNetwork,
				FloatingIPAddress: net1.FloatingIPAddress,
			}
			floatingIPName := fmt.Sprintf("%s_floating_ip%d", container.Name, index)
			resources[floatingIPName] = object.TemplateResource{
				Type:       "OS::Neutron::FloatingIP",
				Properties: FIP,
			}
		}
		serverNetworks = append(serverNetworks, object.ServerNetwork{Port: object.GetResource{GetResource: portName}})
	}
	return
	//object.ServerNetworkParam{NetworkParam: container.Networks}
}

func (s OrchestrationStack) stackTemplate() {
	//fmt.Println(s.Template)
}

func (s OrchestrationStack) checkServers(server ServerParam) error {
	valid := validator.New()
	err := valid.Struct(server)
	if err != nil {
		return err
	}
	return nil
}

func (s OrchestrationStack) filterResponse(stack *stacks.RetrievedStack) {
	return
}

func (s OrchestrationStack) getServerBdms(serverID interface{}) []volumeattach.VolumeAttachment {
	serverClient := client.GetServerClient()
	allPagers, err := volumeattach.List(serverClient, myCommon.Strval(serverID)).AllPages()
	if err == nil {
		ret, err := volumeattach.ExtractVolumeAttachments(allPagers)
		if err == nil {
			return ret
		}

	}
	return nil
}

func (s OrchestrationStack) validateAll() error {
	for _, value := range s.Servers {
		err := s.checkServers(value)
		if err != nil {
			return err
		}
	}
	return nil
}
